﻿using System;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

using SunXNA.RainbowXNA.Material;
using SunXNA.RainbowXNA.Batch;

namespace SunXNA.RainbowXNA.RenderObject
{
    class CStaticMeshRenderInstance : CRainbowXNA.CRenderInstance
    {
        public CStaticMeshRenderInstance(CRainbowXNA.RenderObjectHandleType handle)
            : base(handle)
        {
            //ObjectHandle = handle;
            //World = Matrix.Identity;

            //m_UID = ++s_LastUID;

            //m_rTimeStamp = -1.0f;
            //m_material = new SunXNA.RainbowXNA.Material.CFlatShadedMaterial();

            //if (m_objectHandle != null)
            //{
            //    m_obb = OBB.CreateFromBox(m_objectHandle.GraphicsBox);
            //}
            //else
            //{
            //    m_obb = null;
            //}
        }

        private CRainbowXNA.CRenderInstance[] m_meshPartInstances;

        public CRainbowXNA.CRenderInstance[] MeshPartInstances
        {
            get
            {
                return m_meshPartInstances;
            }

            set
            {
                m_meshPartInstances = value;
            }
        }
    }

    class CStaticMeshRenderObject : IBaseRenderObject
    {
        #region Public Types
        #endregion

        #region Private Member Variables

        protected Model m_model;
        protected BoundingSphere m_graphicsVolume;
        protected BoundingBox m_box;

        protected CRainbowXNA.RenderObjectHandleType[] m_meshPartHandles;
        protected Matrix[] m_boneTransforms;
        protected CRainbowXNA.TextureObjectHandleType[] m_meshPartDefaultTextureHandles;

        #endregion

        #region Public Accessors

        public BoundingSphere Volume
        {
            get
            {
                return m_graphicsVolume;
            }
        }

        public BoundingBox Box
        {
            get
            {
                return m_box;
            }
        }

        #endregion

        #region Public Member Functions

        protected CStaticMeshRenderObject()
        {
        }

        public CStaticMeshRenderObject(ContentManager contentManager, string AssetName, CRainbowXNA rainbow)
        {
            m_model = contentManager.Load<Model>(AssetName);

            m_graphicsVolume = m_model.Meshes[0].BoundingSphere;
            m_box = BoundingBox.CreateFromSphere(m_model.Meshes[0].BoundingSphere);

            int totalMeshParts = 0;
            foreach (ModelMesh mesh in m_model.Meshes)
            {
                BoundingSphere meshBoundingSphere = mesh.BoundingSphere;
                BoundingBox meshBoundingBox = BoundingBox.CreateFromSphere(m_model.Meshes[0].BoundingSphere);

                BoundingSphere.CreateMerged(    ref m_graphicsVolume,
                                                ref meshBoundingSphere,
                                                out m_graphicsVolume);

                BoundingBox.CreateMerged(ref m_box,
                                         ref meshBoundingBox,
                                         out m_box);

                totalMeshParts += mesh.MeshParts.Count;

                Trace.Assert(mesh.MeshParts.Count == mesh.Effects.Count);
            }

            m_meshPartHandles = new CRainbowXNA.RenderObjectHandleType[totalMeshParts];
            m_meshPartDefaultTextureHandles = new CRainbowXNA.TextureObjectHandleType[totalMeshParts];

            int i = 0;
            foreach (ModelMesh mesh in m_model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    if (effect.Texture != null)
                    {
                        m_meshPartDefaultTextureHandles[i] = rainbow.HandleTexture(effect.Texture);
                    }
                }

                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    m_meshPartHandles[i] = rainbow.HandleMeshPart(mesh, part);
                }

                ++i;
            }

            m_boneTransforms = new Matrix[m_model.Bones.Count];
            m_model.CopyAbsoluteBoneTransformsTo(m_boneTransforms);
        }

        public virtual void Draw(GraphicsDevice device, Matrix m4World, Matrix m4View, Matrix m4Projection)
        {
            //Matrix[] limbPositions = new Matrix[m_model.Bones.Count];
            //m_model.CopyAbsoluteBoneTransformsTo(limbPositions);

            //foreach (ModelMesh mesh in m_model.Meshes)
            //{
            //    foreach (BasicEffect effect in mesh.Effects)
            //    {
            //        effect.EnableDefaultLighting();
            //        effect.Projection = m4Projection;
            //        effect.View = m4View;
            //        effect.World = limbPositions[mesh.ParentBone.Index] * m4World;
            //    }

            //    mesh.Draw();
            //}
        }

        public virtual void AddToBatch(CBatchManager batchManager, CRainbowXNA.CRenderInstance instance)
        {
            Trace.Assert(instance is CStaticMeshRenderInstance);
            CStaticMeshRenderInstance modelInstance = (CStaticMeshRenderInstance)instance;

            for (int i = 0; i < modelInstance.MeshPartInstances.Length; ++i)
            {
                CRainbowXNA.CRenderInstance meshPartInstance = modelInstance.MeshPartInstances[i];
                CModelMeshPartRenderObject meshPartRenderObject = (CModelMeshPartRenderObject)meshPartInstance.ObjectHandle.RenderObject;
                meshPartInstance.World = m_boneTransforms[meshPartRenderObject.Mesh.ParentBone.Index] * modelInstance.World;

                meshPartInstance.ObjectHandle.RenderObject.AddToBatch(batchManager, meshPartInstance);
            }
        }

        public virtual CRainbowXNA.CRenderInstance CreateDefaultInstance(CRainbowXNA.RenderObjectHandleType handle, CRainbowXNA rainbow)
        {
            Trace.Assert(handle.RenderObject == this);

            CStaticMeshRenderInstance renderInstance = new CStaticMeshRenderInstance(handle);

            renderInstance.MeshPartInstances = new CRainbowXNA.CRenderInstance[m_meshPartHandles.Length];

            for (int i = 0; i < m_meshPartHandles.Length; ++i)
            {
                renderInstance.MeshPartInstances[i] = rainbow.CreateRenderInstance(m_meshPartHandles[i]);
                //m_meshPartHandles[i].RenderObject.CreateDefaultInstance(m_meshPartHandles[i], rainbow);
                renderInstance.MeshPartInstances[i].Material = new CTexturedLambertShadedMaterial();

                renderInstance.MeshPartInstances[i].Material.Diffuse = Color.White.ToVector4();
                renderInstance.MeshPartInstances[i].Material.TextureHandle = m_meshPartDefaultTextureHandles[i];
            }

            return renderInstance;
        }

        #endregion
    }
}
